﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.VersionControl.Client;
using Microsoft.TeamFoundation.WorkItemTracking.Client;
using OfficeOpenXml;
using OfficeOpenXml.Style;

namespace Core
{
    class DailyReport : ReportBase
    {
        //private ReviewNotificator reviewNotificator;
        private ExcelPackage excelPackage;

        public DailyReport(TfsTeamProjectCollection tfspc, ExcelPackage excelPackage)
        {
            this.excelPackage = excelPackage;
            //reviewNotificator = new ReviewNotificator(tfspc, excelPackage);
        }

        public void Build(WorkItemCollection workItemCollection)
        {
            var date = Configuration.ReportDate.Date;
            while (date < Configuration.ReportFinalDate.Date)
            {
                Trace.WriteLine(string.Format("Building daily report for {0}", date.ToShortDateString()));

                BuildReport(workItemCollection, date);

                date = date.AddDays(1);
            }
        }

        private void BuildReport(WorkItemCollection workItemsList, DateTime date)
        {
            //Add the Content sheet
            if (excelPackage.Workbook.Worksheets.Count == 1)
                excelPackage.Workbook.Worksheets.Add("TempStubForDelete");
            string currentDate  = date.Date.ToString("dd MM yyyy");
            if (excelPackage.Workbook.Worksheets.FirstOrDefault(w => w.Name.Equals(currentDate)) != null)
            {
                excelPackage.Workbook.Worksheets.Delete(currentDate);
            }
            var ws = excelPackage.Workbook.Worksheets.Add(currentDate);
            if (excelPackage.Workbook.Worksheets.FirstOrDefault(w => w.Name == "TempStubForDelete") != null)
                excelPackage.Workbook.Worksheets.Delete("TempStubForDelete");

            ws.Cells[ReportSettings.HeaderRow, ReportSettings.IdColumn].Value = "ID";
            ws.Cells[ReportSettings.HeaderRow, ReportSettings.RevColumn].Value = "Rev";
            ws.Cells[ReportSettings.HeaderRow, ReportSettings.WorkItemTypeColumn].Value = "Work Item Type";
            ws.Cells[ReportSettings.HeaderRow, ReportSettings.TitleColumn].Value = "Title";
            ws.Column(ReportSettings.TitleColumn).Width = 35;
            ws.Column(ReportSettings.AssignedToColumn).Width = 10;
            ws.Cells[ReportSettings.HeaderRow, ReportSettings.AssignedToColumn].Value = "Assigned To";
            ws.Cells[ReportSettings.HeaderRow, ReportSettings.StateColumn].Value = "State";
            ws.Cells[ReportSettings.HeaderRow, ReportSettings.OriginalEstimateColumn].Value = "Original Estimate";
            ws.Cells[ReportSettings.HeaderRow, ReportSettings.CompletedWorkColumn].Value = "Completed Work";
            ws.Cells[ReportSettings.HeaderRow, ReportSettings.RemainingWorkColumn].Value = "Remaining Work";
            ws.Cells[ReportSettings.HeaderRow, ReportSettings.StackRankColumn].Value = "Stack Rank";
            ws.Cells[ReportSettings.HeaderRow, ReportSettings.WasChangedByColumn].Value = "Was Changed By";
            ws.Column(ReportSettings.WasChangedByColumn).Width = 16;
            ws.Cells[ReportSettings.HeaderRow, ReportSettings.WasChangedDateColumn].Value = "Was Changed Date";
            ws.Column(ReportSettings.WasChangedDateColumn).Width = 16;

            ws.Column(ReportSettings.WasChangedDateColumn).Style.Numberformat.Format =
                excelPackage.Workbook.Styles.NumberFormats.Where(f => f.NumFmtId == 22).FirstOrDefault().Format;

            ws.Cells[ReportSettings.HeaderRow, ReportSettings.CompletedWorkChangedColumn].Value = "Completed Work Changed";
            ws.Cells[ReportSettings.HeaderRow, ReportSettings.RemainingWorkChangedColumn].Value = "Remaining Work Changed";
            ws.Cells[ReportSettings.HeaderRow, ReportSettings.ChangesetsColumn].Value = "Changesets";
            ws.Cells[ReportSettings.HeaderRow, ReportSettings.DeltaColumn].Value = "Delta";
            ws.Cells[ReportSettings.HeaderRow, ReportSettings.HistoryColumn].Value = "History";

            ws.Row(ReportSettings.HeaderRow).Style.Fill.PatternType = OfficeOpenXml.Style.ExcelFillStyle.Solid;
            ws.Row(ReportSettings.HeaderRow).Style.Fill.BackgroundColor.SetColor(Color.FromKnownColor(KnownColor.MediumTurquoise));

            Dictionary<string, double> completedSum = new Dictionary<string, double>();
            Dictionary<string, double> remainingSum = new Dictionary<string, double>();
            foreach (var developer in Configuration.Team)
            {
                completedSum.Add(developer.Name, 0);
                remainingSum.Add(developer.Name, 0);
            }

            int row = 0;
            for (int i = 0; i < workItemsList.Count; i++)
            {
                // Fill row
                if (Configuration.ReportDate.Date == date)
                    Trace.WriteLine(string.Format("{0} of {1}.Processing work item #{2}", i, workItemsList.Count, workItemsList[i].Id));

                Revision revisionPrev = null;
                for (int j = 0; j < workItemsList[i].Revisions.Count; j++)
                {
                    Revision revision = workItemsList[i].Revisions[j];
                    if (j != 0)
                        revisionPrev = workItemsList[i].Revisions[j - 1];
                    if (((DateTime)revision.Fields[CoreField.ChangedDate].Value).Date != date ||
                        !Configuration.Team.ContainsName(revision.Fields[CoreField.ChangedBy].Value.ToString()))
                        continue;

                    ws.Cells[row + ReportSettings.FirstContentRow, ReportSettings.IdColumn].Value =
                        revision.Fields[CoreField.Id].Value;
                    ws.Cells[row + ReportSettings.FirstContentRow, ReportSettings.RevColumn].Value =
                        revision.Fields[CoreField.Rev].Value;
                    ws.Cells[row + ReportSettings.FirstContentRow, ReportSettings.WorkItemTypeColumn].Value =
                        revision.Fields[CoreField.WorkItemType].Value;
                    ws.Cells[row + ReportSettings.FirstContentRow, ReportSettings.TitleColumn].Value =
                        revision.Fields[CoreField.Title].Value;
                    ws.Cells[row + ReportSettings.FirstContentRow, ReportSettings.AssignedToColumn].Value =
                        revision.Fields[CoreField.AssignedTo].Value.ToString();
                    ws.Cells[row + ReportSettings.FirstContentRow, ReportSettings.StateColumn].Value =
                        revision.Fields[CoreField.State].Value;
                    if (workItemsList[i].Type.FieldDefinitions.TryGetByName(ReportSettings.OriginalEstimateName) != null)
                    {
                        ws.Cells[row + ReportSettings.FirstContentRow, ReportSettings.OriginalEstimateColumn].Value =
                            revision.Fields[ReportSettings.OriginalEstimateName].Value;
                    }
                    if (workItemsList[i].Type.FieldDefinitions.TryGetByName(ReportSettings.CompletedWorkName) != null)
                    {
                        ws.Cells[row + ReportSettings.FirstContentRow, ReportSettings.CompletedWorkColumn].Value =
                            revision.Fields[ReportSettings.CompletedWorkName].Value;
                    }
                    if (workItemsList[i].Type.FieldDefinitions.TryGetByName(ReportSettings.RemainingWorkName) != null)
                    {
                        ws.Cells[row + ReportSettings.FirstContentRow, ReportSettings.RemainingWorkColumn].Value =
                            revision.Fields[ReportSettings.RemainingWorkName].Value;
                    }
                    if (workItemsList[i].Type.FieldDefinitions.TryGetByName(ReportSettings.StackRankName) != null)
                    {
                        ws.Cells[row + ReportSettings.FirstContentRow, ReportSettings.StackRankColumn].Value =
                            revision.Fields[ReportSettings.StackRankName].Value;
                    }

                    ws.Cells[row + ReportSettings.FirstContentRow, ReportSettings.WasChangedByColumn].Value =
                        revision.Fields[CoreField.ChangedBy].Value;
                    ws.Cells[row + ReportSettings.FirstContentRow, ReportSettings.WasChangedDateColumn].Value =
                        revision.Fields[CoreField.ChangedDate].Value;

                    if (workItemsList[i].Type.FieldDefinitions.TryGetByName(ReportSettings.CompletedWorkName) != null)
                    {
                        double cwNew = revision.Fields[ReportSettings.CompletedWorkName].Value != null
                                       ? (double)revision.Fields[ReportSettings.CompletedWorkName].Value
                                       : 0;
                        double cwOld = 0;
                        if (revisionPrev != null)
                            cwOld = revisionPrev.Fields[ReportSettings.CompletedWorkName].Value != null
                                           ? (double)revisionPrev.Fields[ReportSettings.CompletedWorkName].Value
                                           : 0;
                        double rwNew = revision.Fields[ReportSettings.RemainingWorkName].Value != null
                                           ? (double)revision.Fields[ReportSettings.RemainingWorkName].Value
                                           : 0;
                        double rwOld = 0;
                        if (revisionPrev != null)
                            rwOld = revisionPrev.Fields[ReportSettings.RemainingWorkName].Value != null
                                           ? (double)revisionPrev.Fields[ReportSettings.RemainingWorkName].Value
                                           : 0;

                        ws.Cells[row + ReportSettings.FirstContentRow, ReportSettings.CompletedWorkChangedColumn].Value =
                            cwNew - cwOld;
                        ws.Cells[row + ReportSettings.FirstContentRow, ReportSettings.RemainingWorkChangedColumn].Value =
                            rwNew - rwOld;

                        // Collect statistic
                        completedSum[revision.Fields[CoreField.ChangedBy].Value.ToString()] += cwNew - cwOld;
                        remainingSum[revision.Fields[CoreField.ChangedBy].Value.ToString()] += rwNew - rwOld;
                    }

                    ws.Cells[row + ReportSettings.FirstContentRow, ReportSettings.ChangesetsColumn].Value =
                        GetChangeset(revision, revisionPrev);

                    ws.Cells[row + ReportSettings.FirstContentRow, ReportSettings.HistoryColumn].Value =
                        revision.Fields[CoreField.History].Value;

                    if (StateClosedResolved(revision.Fields[CoreField.State].Value.ToString()))
                    {
                        if (workItemsList[i].Type.FieldDefinitions.TryGetByName(ReportSettings.OriginalEstimateName) !=
                            null &&
                            revision.Fields[ReportSettings.OriginalEstimateName].Value != null &&
                            workItemsList[i].Type.FieldDefinitions.TryGetByName(ReportSettings.CompletedWorkName) != null &&
                            revision.Fields[ReportSettings.CompletedWorkName].Value != null)
                        {
                            ws.Cells[row + ReportSettings.FirstContentRow, ReportSettings.DeltaColumn].Value =
                                (double) revision.Fields[ReportSettings.CompletedWorkName].Value/
                                (double) revision.Fields[ReportSettings.OriginalEstimateName].Value;
                        }
                    }

                    row++;
                }
            }

            BuildDynamics(date, completedSum, remainingSum, workItemsList);
        }

        private void BuildDynamics(DateTime date, Dictionary<string, double> completedSum, Dictionary<string, double> remainingSum, WorkItemCollection workItemList)
        {

            // Prepare data

            Dictionary<string, double> compeltedInResolvedTasksSum = new Dictionary<string, double>();
            Dictionary<string, double> originalCoveredInResolvedTasksSum = new Dictionary<string, double>();
            foreach (var developer in Configuration.Team)
            {
                compeltedInResolvedTasksSum.Add(developer.Name, 0);
                originalCoveredInResolvedTasksSum.Add(developer.Name, 0);
            }

            foreach (WorkItem workItem in workItemList)
            {
                if (!StateClosedResolved(workItem.State) ||
                    !ClosedInDate(workItem, date))
                    continue;

                double completedTotal = 0;
                if (workItem.Type.FieldDefinitions.TryGetByName(ReportSettings.CompletedWorkName) != null &&
                    workItem.Fields[ReportSettings.CompletedWorkName].Value != null)
                    completedTotal = (double)workItem.Fields[ReportSettings.CompletedWorkName].Value;
                double orginalTotal = 0;
                if (workItem.Type.FieldDefinitions.TryGetByName(ReportSettings.OriginalEstimateName) != null &&
                    workItem.Fields[ReportSettings.OriginalEstimateName].Value != null)
                    orginalTotal = (double) workItem.Fields[ReportSettings.OriginalEstimateName].Value;

                for (int i = workItem.Revisions.Count - 1; i > 0; i--)
                {
                    var chnagedBy = workItem.Revisions[i].Fields[CoreField.ChangedBy].Value.ToString();
                    if (!Configuration.Team.ContainsName(chnagedBy))
                        continue;

                    double completedInRevision = 0;
                    double completedCurrent = 0;
                    if (workItem.Type.FieldDefinitions.TryGetByName(ReportSettings.CompletedWorkName) != null &&
                        workItem.Revisions[i].Fields[ReportSettings.CompletedWorkName].Value != null)
                        completedCurrent = (double) workItem.Revisions[i].Fields[ReportSettings.CompletedWorkName].Value;
                    double completedPrev = 0;
                    if (workItem.Type.FieldDefinitions.TryGetByName(ReportSettings.OriginalEstimateName) != null &&
                        workItem.Revisions[i - 1].Fields[ReportSettings.CompletedWorkName].Value != null) 
                        completedPrev = (double)workItem.Revisions[i - 1].Fields[ReportSettings.CompletedWorkName].Value;
                    completedInRevision = completedCurrent - completedPrev;

                    double originalCoveredInRevision = (orginalTotal / completedTotal) * completedInRevision;

                    compeltedInResolvedTasksSum[chnagedBy] += completedInRevision;
                    originalCoveredInResolvedTasksSum[chnagedBy] += originalCoveredInRevision;
                }
            }

            // Write to Excel

            var wsDynamics = excelPackage.Workbook.Worksheets[ReportSettings.DynamicsWorksheet];
            int currentColumn = 1;
            DateTime? columnDate = null;
            do
            {
                currentColumn++;
                if (wsDynamics.Cells[1, currentColumn].Value is DateTime)
                    columnDate = (DateTime)wsDynamics.Cells[1, currentColumn].Value;
            } while (wsDynamics.Cells[1, currentColumn].Text != string.Empty &&
                     (columnDate.HasValue && columnDate.Value.Date != date.Date));

            wsDynamics.Cells[1, currentColumn].Value = date.Date;
            wsDynamics.Column(currentColumn).Width = 9.5;

            for (int i = ReportSettings.CompletedRow; i < ReportSettings.CompletedRow + Configuration.Team.Developers.Count; i++)
            {
                wsDynamics.Cells[i, currentColumn].Value =
                    completedSum[wsDynamics.Cells[i, ReportSettings.NamesColumn].Text];
            }

            for (int i = ReportSettings.RemainingRow; i < ReportSettings.RemainingRow + Configuration.Team.Developers.Count; i++)
            {
                wsDynamics.Cells[i, currentColumn].Value =
                    remainingSum[wsDynamics.Cells[i, ReportSettings.NamesColumn].Text];
            }

            for (int i = ReportSettings.CompeltedInResolvedTasksRow; i < ReportSettings.CompeltedInResolvedTasksRow + Configuration.Team.Developers.Count; i++)
            {
                wsDynamics.Cells[i, currentColumn].Value =
                    compeltedInResolvedTasksSum[wsDynamics.Cells[i, ReportSettings.NamesColumn].Text];
            }

            for (int i = ReportSettings.OriginalCoveredInResolvedTasksRow; i < ReportSettings.OriginalCoveredInResolvedTasksRow + Configuration.Team.Developers.Count; i++)
            {
                wsDynamics.Cells[i, currentColumn].Value =
                    originalCoveredInResolvedTasksSum[wsDynamics.Cells[i, ReportSettings.NamesColumn].Text];
            }

        }

        private bool ClosedInDate(WorkItem workItem, DateTime date)
        {
            for(int i=workItem.Revisions.Count-1; i > 0; i--)
            {
                if (StateClosedResolved(workItem.Revisions[i].Fields[CoreField.State].Value.ToString()) &&
                    !StateClosedResolved(workItem.Revisions[i - 1].Fields[CoreField.State].Value.ToString()) && 
                    ((DateTime)workItem.Revisions[i].Fields[CoreField.ChangedDate].Value).Date == date.Date)
                    return true;
            }
            return false;
        }

        private string GetChangeset(Revision revision, Revision revisionPrev)
        {
            if (revisionPrev == null ||
                revision.Links.Count > revisionPrev.Links.Count)
            {
                foreach (Link newLink in revision.Links)
                {
                    if ((revisionPrev == null ||
                        !revisionPrev.Links.Contains(newLink)) &&
                        newLink is ExternalLink &&
                        newLink.ArtifactLinkType.Name.Contains("Changeset"))
                    {
                        string url = ((ExternalLink)newLink).LinkedArtifactUri;
                        int changesetId = int.Parse(url.Split('/')[url.Split('/').Length - 1]);
                        //reviewNotificator.ProcessChangeset(changesetId);
                        return changesetId.ToString();
                    }
                }
            }
            return string.Empty;
        }

        private bool IsRevisionShouldBeProcessed(Revision revision, DateTime date)
        {
            return ((DateTime)revision.Fields[CoreField.ChangedDate].Value).Date == date &&
                   Configuration.Team.ContainsName(revision.Fields[CoreField.ChangedBy].Value.ToString());
        }
    }
}
